#include "cx_debug.h"
cx_slist_t variables = 
{
    .head = CX_NULL,
    .tail = CX_NULL,
};
cx_slist_t commands = 
{
    .head = CX_NULL,
    .tail = CX_NULL,
};

const char *type_string[12] = 
{
    "int8",
    "uint8",
    "int16",
    "uint16",
    "int32",
    "uint32",
    "int64",
    "uint64",
    "float",
    "double",
    "char",
    "string"
};

#define vartoitem(x) ((cx_slist_item_t*)(x))
#define itemtovar(x) ((cx_debug_variable_t*)(x))
#define cmdtoitem(x) ((cx_slist_item_t*)(x))
#define itemtocmd(x) ((cx_debug_command_t*)(x))

static char * stringPassSpace(char *str)
{
    while((*str == ' '))
    {
        ++str;
    }
    return str;
}

char * getStringField(char *dst, char *src)
{
    int i=0;
    src = stringPassSpace(src);
    for( ; (src[i] != '\0') && (src[i] != ' '); i++)
    {
        dst[i] = src[i];
    }
    dst[i] = '\0';

    if(src[i] == ' ')
    {
        src = stringPassSpace(src + i);
    }
    else
    {
        src = src + i;
    }
    return src;
}

cx_err_t cx_debug_addVariable(cx_debug_variable_t *var)
{
    cx_slist_append(&variables, vartoitem(var));
    return CX_ERR_OK;
}

cx_err_t cx_debug_addCommand(cx_debug_command_t *cmd)
{
    cx_slist_append(&commands, cmdtoitem(cmd));
    return CX_ERR_OK;
}

static void set_value(void *pdata, char *value, cx_datatype_t type)
{
    switch(type)
    {
        case CX_DATATYPE_CHAR:
        case CX_DATATYPE_INT8:*((cx_int8_t*)pdata) = (cx_int8_t)cx_format._DecStringToInt(value);break;
        case CX_DATATYPE_UINT8:*((cx_uint8_t*)pdata) = (cx_uint8_t)cx_format._DecStringToUint(value);break;
        case CX_DATATYPE_INT16:*((cx_int16_t*)pdata) = (cx_int16_t)cx_format._DecStringToInt(value);break;
        case CX_DATATYPE_UINT16:*((cx_uint16_t*)pdata) = (cx_uint16_t)cx_format._DecStringToUint(value);break;
        case CX_DATATYPE_INT32:*((cx_int32_t*)pdata) = (cx_int32_t)cx_format._DecStringToInt(value);break;
        case CX_DATATYPE_UINT32:*((cx_uint32_t*)pdata) = (cx_uint32_t)cx_format._DecStringToUint(value);break;
        case CX_DATATYPE_INT64:*((cx_int64_t*)pdata) = (cx_int32_t)cx_format._DecStringToInt(value);break;
        case CX_DATATYPE_UINT64:*((cx_uint64_t*)pdata) = (cx_uint32_t)cx_format._DecStringToUint(value);break;
        case CX_DATATYPE_FLOAT:*((float*)pdata) = (float)cx_format._StringToFloat(value);break;
        case CX_DATATYPE_DOUBLE:*((double*)pdata) = (float)cx_format._StringToFloat(value);break;
        case CX_DATATYPE_STRING:strcpy(pdata, value);break;
        default:break;
    }
}

void cx_debug_set_handler(char *input)
{
    char buf[30];
    cx_slist_item_t *item = variables.head;
    input = getStringField(buf, input);
    while(item != CX_NULL)
    {
        if(strcmp(itemtovar(item)->name, buf) != 0)
        {
            item = item->next;
        }
        else
        {
            input = getStringField(buf,input);
            set_value(itemtovar(item)->pdata,buf,itemtovar(item)->type);
            break;
        }
    }
}

void cx_debug_cmd_handler(char * input)
{
    char buf[30];
    cx_slist_item_t *item = commands.head;
    input = getStringField(buf, input);
    while(item != CX_NULL)
    {
        if(strcmp(itemtocmd(item)->name, buf) != 0)
        {
            item = item->next;
        }
        else
        {
            input = getStringField(buf,input);
            itemtocmd(item)->handler(buf);
            break;
        }
    }
}

#ifdef PUTC_ENABLED

static void print_varInfo(cx_debug_variable_t *var)
{
    printf("name:%s,type:%s,value:",var->name,type_string[var->type]);
    switch (var->type)
    {
    case CX_DATATYPE_INT8:
        printf("%d",*((cx_int8_t *)(var->pdata)));break;
    case CX_DATATYPE_UINT8:
        printf("%d",*((cx_uint8_t *)(var->pdata)));break;
    case CX_DATATYPE_INT16:
        printf("%d",*((cx_int16_t *)(var->pdata)));break;
    case CX_DATATYPE_UINT16:
        printf("%d",*((cx_uint16_t *)(var->pdata)));break;
    case CX_DATATYPE_INT32:
        printf("%d",*((cx_int32_t *)(var->pdata)));break;
    case CX_DATATYPE_UINT32:
        printf("%d",*((cx_uint32_t *)(var->pdata)));break;
    case CX_DATATYPE_FLOAT:
        printf("%f",*((float *)(var->pdata)));break;
    case CX_DATATYPE_DOUBLE:
        printf("%lf",*((double *)(var->pdata)));break;
    case CX_DATATYPE_CHAR:
        printf("%c",*((char *)(var->pdata)));break;
    case CX_DATATYPE_STRING:
        printf("%s",((char *)(var->pdata)));break;
    default:break;
    }
    putchar('\n');
}

static void cx_debug_get_handler(char *input)
{
    char buf[30];
    cx_slist_item_t *item = variables.head;
    input = getStringField(buf, input);
    while(item != CX_NULL)
    {
        if(strcmp(itemtovar(item)->name, buf) != 0)
        {
            item = item->next;
        }
        else
        {
            print_varInfo(itemtovar(item));
            break;
        }
    }
}
#endif

cx_err_t cx_debug_handler(char *input)
{
    char buf[30];
    cx_err_t ret = CX_ERR_OK;
    input = getStringField(buf, input);
    if(strcmp(buf, "SET") == 0)
    {
        cx_debug_set_handler(input);
    }
#ifdef PUTC_ENABLED
    else if(strcmp(buf, "GET") == 0)
    {
        cx_debug_get_handler(input);
    }
#endif
    else if(strcmp(buf, "CMD") == 0)
    {
        cx_debug_cmd_handler(input);
    }
    else
    {
        ret = CX_ERR_NOMATCH;
    }
    return ret;
}

// #define CX_DEBUG_USE_PC_EXAMPLE

#ifdef CX_DEBUG_USE_PC_EXAMPLE
/**Example**/
void print_all_variable_info(void *args)
{
    cx_slist_item_t *item = variables.head;
    while(item != CX_NULL)
    {
        print_varInfo(itemtovar(item));
        item = item->next;
    }
}

int main(void)
{
    int a = 0;
    int b = 200;
    cx_int16_t PWM_Compare;
    float f;
    char char_buf[100];
    cx_debug_variable_t va = 
    {
        .name = "a",
        .pdata = &a,
        .type = CX_DATATYPE_INT32
    };
    cx_debug_variable_t vb = 
    {
        .name = "b",
        .pdata = &b,
        .type = CX_DATATYPE_INT32
    };
    cx_debug_variable_t cmp = 
    {
        .name = "cmp",
        .pdata = &PWM_Compare,
        .type = CX_DATATYPE_INT16
    };
    cx_debug_variable_t vf = 
    {
        .name = "f",
        .pdata = &f,
        .type = CX_DATATYPE_FLOAT
    };
    cx_debug_command_t getInfo = 
    {
        .name = "getInfo",
        .handler = print_all_variable_info
    };
    cx_debug_addVariable(&va);
    cx_debug_addVariable(&vb);
    cx_debug_addVariable(&cmp);
    cx_debug_addVariable(&vf);
    cx_debug_addCommand(&getInfo);
    // printf("before:\n");
    // cx_debug_handler("CMD getInfo");
    // cx_debug_handler("SET a 100");
    // cx_debug_handler("SET b 1000");
    // cx_debug_handler("SET cmp 3456");
    // cx_debug_handler("SET f 0.2");
    // printf("after:\n");
    // cx_debug_handler("CMD getInfo");
    while(1)
    {
        gets(char_buf);
        printf("Recieve:%s\n",char_buf);
        cx_debug_handler(char_buf);
    }
    return 0;
}
#endif

// #define CX_DEBUG_USE_STM32_EXAMPLE
#ifdef CX_DEBUG_USE_STM32_EXAMPLE
char debug_input[50];
cx_uint8_t current_pos = 0;
void USART2_IRQHandler(void)
{
    if(USART_GetITFlags(USART2, USART_FLAGS_RXE) != RESET)
    {
        debug_input[current_pos] = USART2->DR;
        current_pos++;
        USART_ClearITPendingBit(USART2, USART_FLAGS_RXE);
    }
    if(USART_GetITStatus(USART2, USART_FLAGS_IDLE) != RESET)
    {
        debug_input[current_pos] = '\0';
        cx_debug_handler(debug_input);
        current_pos = 0;
        USART_ClearITPendingBit(USART2, USART_FLAGS_IDLE);
    }
}
#endif
